#include "Parser.h"
#include "GrammarAnalyze.h"
#include "Code.h"
#include "ErrorHandle.h"
#include <iostream>

using namespace std;


CParser::CParser(CScannerPtr pScanner)
:m_pScanner(pScanner)
{
	LOG("Initialize Parser");
}

CParser::~CParser()
{
	LOG("Destroying Parser");
}

int CParser::RegisterProduction( CProductionPtr pProduction )
{

	m_pProductions.push_back(pProduction);

	return 0;
}

void CParser::InitializeStatusTable()
{
	LOG("CParser::InitializeStatusTable Start!");

	CGrammarAnalyze GA(m_pProductions);

	m_ParserStatusTable = GA.Analyze();

	LOG("CParser::InitializeStatusTable End!");
}

void CParser::Parse( std::istream& is )
{
	try
	{

        InitializeStatusTable();

        InitializeParse();

        CCodePtr pCode = CCode::create(is);
        m_pScanner->PushCode(pCode);

        CTokenPtr pToken;

        int TokenConsumed = 1;

		while(1)
		{
			if (TokenConsumed)
			{
				pToken = m_pScanner->GetToken();
				if (!pToken || !pToken->GetType())
				{
					break;
				}
			}

			TokenConsumed = ProcessToken(pToken);

			if (2 == TokenConsumed)
			{
				LOG("Accept");
				break;
			}
		}
	}
	catch (boost::exception& e)
	{
		cerr << diagnostic_information(e);
	}

}

void CParser::InitializeParse()
{
	m_StatusStack.push(0);
}

int CParser::ProcessToken( CTokenPtr pToken )
{
	CParserStatusPtr pStatus = m_ParserStatusTable[m_StatusStack.top()];
	_Action Action = pStatus->GetAction(pToken->GetType());
	if (Action.Action == ACTION_SHIFT)
	{
		m_StatusStack.push(Action.StatusIndex);
		m_TokenStack.push(pToken);
		return 1;
	}
	else if (Action.Action == ACTION_REDUCE)
	{
		CProductionPtr pProduction = m_pProductions[Action.ProductionIndex];

		int ArgLength = pProduction->ArgLength();
        CProductionArgPtr pArg = CProductionArg::create(ArgLength);

        for (int i = ArgLength - 1 ; i >= 0; --i)
        {
            pArg->SetArg(i, m_TokenStack.top());    
            m_TokenStack.pop();
            m_StatusStack.pop();
        }

		CTokenPtr pProducedToken = pProduction->Reduce(pArg);
		

		CParserStatusPtr pReducedStatus = m_ParserStatusTable[m_StatusStack.top()];
		_GOTO Goto = pReducedStatus->GetGoto(pProducedToken->GetType());
		if (Goto.StatusIndex != -1)
		{
			m_TokenStack.push(pProducedToken);
			m_StatusStack.push(Goto.StatusIndex);
		}
		return 0;
	}
	else if (Action.Action == ACTION_ACCEPT)
	{
		return 2;
	}
	else
	{
		LOG("CParser::ProcessToken Error");
		throw exception_base() << error_message("Token Error");
	}
}